//
//  Func.swift
//  App
//
//  Created by skywalker on 2024/3/30.
//

import Foundation
import CommonCrypto

class XC {

    let cghData = "fcad6bf8a9e7fe33b251ffc81c1528da"
    let AK = "443322114433221144332211000000004433221144332211"
    
//    override func viewDidLoad() {
//        super.viewDidLoad()
//        //解密,我这里是对数据流处理，如果需要对字符串处理，data用string.data(using...)方法获取，加密也是一样，下文HMAC_SHA加密同理
//        let a = decryptWith3DES_ECB(decryptData: cghData.hexadecimal()!, keyData: AK.hexadecimal()!)
//        print(a?.hexString())
//
//        //加密
//        let b = encryptWith3DES_ECB(encryptData: a!, keyData: AK.hexadecimal()!)
//        print(b?.hexString())
//    }

    ///3DES_ECB加密
    func encryptWith3DES_ECB(encryptData:Data, keyData:Data) -> Data?{
        
        let myKeyData : Data = keyData
        var myRawData : Data = encryptData
        let buffer_size : size_t = myRawData.count + kCCBlockSize3DES
        let buffer = UnsafeMutablePointer<NSData>.allocate(capacity: buffer_size)
        var num_bytes_encrypted : size_t = 0
        let operation: CCOperation = UInt32(kCCEncrypt)
        let algoritm:  CCAlgorithm = UInt32(kCCAlgorithm3DES)
        let options:   CCOptions   = UInt32(kCCOptionECBMode)
        
//        CCCryptorCreateFromData
//        let options:   CCOptions   = UInt32(kCCOptionCBCMode)
        let keyLength        = size_t(kCCKeySize3DES)
        let Crypto_status: CCCryptorStatus = CCCrypt(operation, algoritm, options, (myKeyData as NSData).bytes, keyLength, nil, (myRawData as NSData).bytes, myRawData.count, buffer, buffer_size, &num_bytes_encrypted)
        if UInt32(Crypto_status) == UInt32(kCCSuccess){
            let myResult: Data = Data(bytes: buffer, count: num_bytes_encrypted)
            free(buffer)
            return myResult
        }else{
            free(buffer)
            return nil
        }
    }
    
    ///3DES_ECB解密
    func decryptWith3DES_ECB(decryptData: Data, keyData: Data) -> Data?{
        
        let mydata_len : Int = decryptData.count
        let keyData : Data = keyData
        let buffer_size : size_t = mydata_len+kCCBlockSize3DES
        let buffer = UnsafeMutablePointer<NSData>.allocate(capacity: buffer_size)
        var num_bytes_encrypted : size_t = 0
        let operation: CCOperation = UInt32(kCCDecrypt)
        let algoritm:  CCAlgorithm = UInt32(kCCAlgorithm3DES)
        let options:   CCOptions   = UInt32(kCCOptionECBMode)
        let keyLength        = size_t(kCCKeySize3DES)
        let decrypt_status : CCCryptorStatus = CCCrypt(operation, algoritm, options, (keyData as NSData).bytes, keyLength, nil, (decryptData as NSData).bytes, mydata_len, buffer, buffer_size, &num_bytes_encrypted)
        if UInt32(decrypt_status) == UInt32(kCCSuccess){
            
            let myResult : Data = Data(bytes: buffer, count: num_bytes_encrypted)
            free(buffer)
            return myResult
        }else{
            free(buffer)
            return nil
        }
    }

}

extension String{
    
    //HexString转Data
    func hexadecimal() -> Data? {
//        var data = Data(capacity: characters.count / 2)
        var data = Data(capacity: self.count / 2)
        let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive)
        regex.enumerateMatches(in: self, range: NSMakeRange(0, utf16.count)) { match, flags, stop in
            guard let match = match else { return }
            let byteString = (self as NSString).substring(with: match.range)
            var num = UInt8(byteString, radix: 16)!
            data.append(&num, count: 1)
        }
        
        guard data.count > 0 else { return nil }
        
        return data
    }
    
}

extension Data{
    
    ///Data转HexString
    func hexString() -> String {
        return self.withUnsafeBytes({ (bytes: UnsafePointer<UInt8>) -> String in
            let buffer = UnsafeBufferPointer(start: bytes, count: self.count)
            return buffer.map{String(format: "%02hhx", $0)}.reduce("", {$0 + $1})
        })
    }
}

//HMAC-SHA加密
enum CryptoAlgorithm {
    case MD5, SHA1, SHA224, SHA256, SHA384, SHA512
    
    var HMACAlgorithm: CCHmacAlgorithm {
        var result: Int = 0
        switch self {
        case .MD5:      result = kCCHmacAlgMD5
        case .SHA1:     result = kCCHmacAlgSHA1
        case .SHA224:   result = kCCHmacAlgSHA224
        case .SHA256:   result = kCCHmacAlgSHA256
        case .SHA384:   result = kCCHmacAlgSHA384
        case .SHA512:   result = kCCHmacAlgSHA512
        }
        return CCHmacAlgorithm(result)
    }
    
    var digestLength: Int {
        var result: Int32 = 0
        switch self {
        case .MD5:      result = CC_MD5_DIGEST_LENGTH
        case .SHA1:     result = CC_SHA1_DIGEST_LENGTH
        case .SHA224:   result = CC_SHA224_DIGEST_LENGTH
        case .SHA256:   result = CC_SHA256_DIGEST_LENGTH
        case .SHA384:   result = CC_SHA384_DIGEST_LENGTH
        case .SHA512:   result = CC_SHA512_DIGEST_LENGTH
        }
        return Int(result)
    }
}

extension String {
///HMAC_SHA
    func hmac(algorithm: CryptoAlgorithm, key: String) -> String {
        let str = (self.hexadecimal()! as NSData).bytes//self.cString(using: String.Encoding.utf8)
        let strLen = (self.hexadecimal()!).count//Int(self.lengthOfBytes(using: String.Encoding.utf8))
        let digestLen = algorithm.digestLength
        let result = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLen)
        let keyStr = (key.hexadecimal()! as NSData).bytes//key.cString(using: String.Encoding.utf8)
        let keyLen = (key.hexadecimal()!).count//Int(key.lengthOfBytes(using: String.Encoding.utf8))
        
        CCHmac(algorithm.HMACAlgorithm, keyStr, keyLen, str, strLen, result)
        
        let digest = stringFromResult(result: result, length: digestLen)
        
        result.deallocate()
//        result.deallocate(capacity: digestLen)
        
        return digest
    }
    
    private func stringFromResult(result: UnsafeMutablePointer<CUnsignedChar>, length: Int) -> String {
        let hash = NSMutableString()
        for i in 0..<length {
            hash.appendFormat("%02x", result[i])
        }
        return String(hash)
    }
}

//作者：T92
//链接：https://www.jianshu.com/p/682a218b27af
//来源：简书
//著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。
